﻿<title>Audio + Canvas2D/HTML recording using RecordRTC!</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="author" type="text/html" href="https://plus.google.com/+MuazKhan">
<meta name="author" content="Muaz Khan">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<style>
    body {overflow-x:hidden;background: rgb(233, 233, 233);}
    #elementToShare {
        background: rgb(233, 233, 233);
        font-size: 2em;
        height: 100%;
        left: 0;
        padding: 0 1em;
        position: absolute;
        top: 0;
        width: 100%;
    }

    input, textarea {
        border: 1px solid red;
        font-size: 1em;
        outline: none;
        padding: .3em .8em;
    }

    button, input[type=button] {
        -moz-border-radius: 3px;
        -moz-transition: none;
        -webkit-transition: none;
        background: #0370ea;
        background: -moz-linear-gradient(top, #008dfd 0, #0370ea 100%);
        background: -webkit-linear-gradient(top, #008dfd 0, #0370ea 100%);
        border: 1px solid #076bd2;
        border-radius: 3px;
        color: #fff;
        display: inline-block;
        font-family: inherit;
        font-size: .8em;
        font-size: 1.5em;
        line-height: 1.3;
        padding: 5px 12px;
        text-align: center;
        text-shadow: 1px 1px 1px #076bd2;
    }

    button:hover, input[type=button]:hover { background: rgb(9, 147, 240); }

    button:active, input[type=button]:active { background: rgb(10, 118, 190); }

    button[disabled], input[type=button][disabled] {
        background: none;
        border: 1px solid rgb(187, 181, 181);
        color: gray;
        text-shadow: none;
    }
    a {
        color: #2844FA;
        cursor: pointer;
        text-decoration: none;
    }
    a:hover, a:focus { color: #1B29A4; }
    a:active { color: #000; }
</style>

<div id="elementToShare" contenteditable style="margin-top:3px;">
    <h2>
		Audio + Canvas2D/HTML recording <br />using <a href="https://www.webrtc-experiment.com/RecordRTC/" contenteditable="false">RecordRTC</a> and <a href="https://www.webrtc-experiment.com/ffmpeg/" contenteditable="false">ffmpeg</a>!
	</h2>
    <h3>Content is edit-able.</h3>
    <input type="text" value="Type Text!" />
    <br />
    <textarea>resize textarea</textarea>
    <br /><br />
    <ol>
        <li>This demo uses <a href="https://www.webrtc-experiment.com/Pre-recorded-Media-Streaming/" contenteditable="false">HTML2Canvas Library</a> along with <a href="https://www.webrtc-experiment.com/RecordRTC/" contenteditable="false">RecordRTC</a> to record/encode HTML snapshots (webp images) in WebM container.</li>
        <li>It also uses <a href="https://www.webrtc-experiment.com/RecordRTC/" contenteditable="false">RecordRTC</a> to record audio in WAV container.</li>
        <li>Then it uses <a href="https://www.webrtc-experiment.com/ffmpeg/" contenteditable="false">ffmpeg-asm.js</a> to transcode and merge both WAV/WebM in single MP4 container.</li>
    </ol>
</div>

<div style="position: fixed;top;0;left: 20%;right: 20%;text-align: center;">
    <button id="start" contenteditable="false">Start Audio+Canvas Recording</button>
    <button id="stop" disabled contenteditable="false">Stop</button>
</div>

<script src="https://cdn.webrtc-experiment.com/screenshot.js"> </script>
<script src="https://cdn.webrtc-experiment.com/RecordRTC.js"> </script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

<script>
var elementToShare = document.getElementById('elementToShare');
var canvas2d = document.createElement('canvas');
var context = canvas2d.getContext('2d');

canvas2d.width = elementToShare.clientWidth;
canvas2d.height = elementToShare.clientHeight;

canvas2d.style.top = 0;
canvas2d.style.left = 0;
canvas2d.style.zIndex = -1;
(document.body || document.documentElement).appendChild(canvas2d);

var isRecordingStarted = false;
var isStoppedRecording = false;

(function looper() {
    if(!isRecordingStarted) {
        return setTimeout(looper, 500);
    }

    html2canvas(elementToShare, {
        grabMouse: true,
        onrendered: function(canvas) {
            context.clearRect(0, 0, canvas2d.width, canvas2d.height);
            context.drawImage(canvas, 0, 0, canvas2d.width, canvas2d.height);

            if(isStoppedRecording) {
                return;
            }

            setTimeout(looper, 1);
        }
    });
})();

var canvasRecorder = RecordRTC(canvas2d, {
    type: 'canvas'
});

var recordAudio;

document.getElementById('start').onclick = function() {
    this.disabled = true;
    navigator.getUserMedia({ audio: true }, function(stream) {
        var audio = document.createElement('audio');
        audio.muted = true;
        audio.volume = 0;
        audio.srcObject = stream;

        recordAudio = RecordRTC(stream, {
            type: 'audio',
			recorderType: StereoAudioRecorder
        });

        isStoppedRecording = false;
        isRecordingStarted = true;

        canvasRecorder.startRecording();
        recordAudio.startRecording();

        document.getElementById('stop').disabled = false;
    }, function(error) { log( JSON.stringify ( error ) ); });
};
document.getElementById('stop').onclick = function() {
    this.disabled = true;
    recordAudio.stopRecording(function() {
        isStoppedRecording = true;
        canvasRecorder.stopRecording(function() {
            convertStreams(canvasRecorder.getBlob(), recordAudio.getBlob());
            log('<a href="'+ workerPath +'" download="ffmpeg-asm.js">ffmpeg-asm.js</a> file download started. It is about 18MB in size; please be patient!');
        });
    });

    document.getElementById('start').disabled = false;
};

var workerPath = 'https://archive.org/download/ffmpeg_asm/ffmpeg_asm.js';

if(document.domain == 'localhost') {
	workerPath = location.href.replace(location.href.split('/').pop(), '') + 'ffmpeg_asm.js';
}

function processInWebWorker() {
    var blob = URL.createObjectURL(new Blob(['importScripts("' + workerPath + '");var now = Date.now;function print(text) {postMessage({"type" : "stdout","data" : text});};onmessage = function(event) {var message = event.data;if (message.type === "command") {var Module = {print: print,printErr: print,files: message.files || [],arguments: message.arguments || [],TOTAL_MEMORY: 268435456};postMessage({"type" : "start","data" : Module.arguments.join(" ")});postMessage({"type" : "stdout","data" : "Received command: " +Module.arguments.join(" ") +((Module.TOTAL_MEMORY) ? ".  Processing with " + Module.TOTAL_MEMORY + " bits." : "")});var time = now();var result = ffmpeg_run(Module);var totalTime = now() - time;postMessage({"type" : "stdout","data" : "Finished processing (took " + totalTime + "ms)"});postMessage({"type" : "done","data" : result,"time" : totalTime});}};postMessage({"type" : "ready"});'], {
        type: 'application/javascript'
    }));

    var worker = new Worker(blob);
    URL.revokeObjectURL(blob);
    return worker;
}

var worker;

function convertStreams(videoBlob, audioBlob) {
    var vab;
    var aab;
    var buffersReady;
    var workerReady;
    var posted = false;

    var fileReader1 = new FileReader();
    fileReader1.onload = function() {
        vab = this.result;

        if (aab) buffersReady = true;

        if (buffersReady && workerReady && !posted) postMessage();
    };
    var fileReader2 = new FileReader();
    fileReader2.onload = function() {
        aab = this.result;

        if (vab) buffersReady = true;

        if (buffersReady && workerReady && !posted) postMessage();
    };

    fileReader1.readAsArrayBuffer(videoBlob);
    fileReader2.readAsArrayBuffer(audioBlob);

    if (!worker) {
        worker = processInWebWorker();
    }

    worker.onmessage = function(event) {
        var message = event.data;
        if (message.type == "ready") {
            log('<a href="'+ workerPath +'" download="ffmpeg-asm.js">ffmpeg-asm.js</a> file has been loaded.');
            workerReady = true;
            if (buffersReady)
                postMessage();
        } else if (message.type == "stdout") {
            log(message.data);
        } else if (message.type == "start") {
            log('<a href="'+ workerPath +'" download="ffmpeg-asm.js">ffmpeg-asm.js</a> file received ffmpeg command.');
        } else if (message.type == "done") {
            log(JSON.stringify(message));

            var result = message.data[0];
            log(JSON.stringify(result));

            var blob = new Blob([result.data], {
                type: 'video/mp4'
            });

            log(JSON.stringify(blob));

            PostBlob(blob);
        }
    };
    var postMessage = function() {
        posted = true;
		
		/*
			[
                '-i', 'video.webm',
                '-i', 'audio.wav',
				'-s', '1280x720',
                '-c:v', 'mpeg4',
                '-c:a', 'aac',
                '-b:v', '1450k',
                '-b:a', '96k',
				'-bf', '2',
				'-g', '90',
				'-sc_threshold', '0',
				'-ar', '32000',
                '-strict', 'experimental', 'output.mp4'
            ]
		*/
		
		worker.postMessage({
            type: 'command',
            arguments: [
                '-i', 'video.webm',
                '-i', 'audio.wav',
                '-c:v', 'mpeg4',
                '-c:a', 'vorbis', // or aac
                '-b:v', '6400k',  // or 1450k
                '-b:a', '4800k',  // or 96k
                '-strict', 'experimental', 'output.mp4'
            ],
            files: [
                {
                    data: new Uint8Array(vab),
                    name: 'video.webm'
                },
                {
                    data: new Uint8Array(aab),
                    name: "audio.wav"
                }
            ]
        });
    };
}

var h2 = document.querySelector('h2');

function PostBlob(blob) {
    h2.innerHTML = '<a href="' + URL.createObjectURL(blob) + '" target="_blank" download="Recorded Audio+Canvas File.mp4">Download Recorded Audio+Canvas file in MP4 container and play in VLC player!</a>';
    h2.setAttribute('contenteditable', 'false');
}

function log(message) {
    h2.innerHTML = message;
    console.log(message);
}
</script>
<a href="https://www.webrtc-experiment.com/ffmpeg/" style="border-bottom: 1px solid red; color: red; font-size: 1.2em; position: absolute; right: 0; text-decoration: none; top: 0;">←Try other Ffmpeg.js and RecordRTC demos</a>
